﻿using System;
using System.ComponentModel;
using System.Diagnostics;
using System.IO.IsolatedStorage;
using System.Windows;
using System.Windows.Media;
using System.Xml.Serialization;
using Microsoft.Practices.EnterpriseLibrary.ExceptionHandling;
using Microsoft.Practices.EnterpriseLibrary.Logging;
using System.Collections.Generic;
using Tomers.WPF.Localization;

namespace XmlStudio.Settings {

    /// <summary>
    /// Helper for storing language in settings.
    /// </summary>
    [Serializable]
    public class LanguageSetting {
        /// <summary>
        /// Gets or sets the value.
        /// </summary>
        /// <value>The value.</value>
        public string Value { get; set; }
        /// <summary>
        /// Gets or sets the display name.
        /// </summary>
        /// <value>The display name.</value>
        public string DisplayName { get; set; }

        /// <summary>
        /// Overriden so two structure items are compared by their content.
        /// </summary>
        /// <param name="obj">Another instance to compare.</param>
        /// <returns>True if both instances have the same content, false otherwise.</returns>
        public override bool Equals(object obj) {
            if(!(obj is LanguageSetting))
                return false;

            if(obj == null)
                return false;

            LanguageSetting t = (LanguageSetting)obj;

            return (Value.Equals(t.Value) && DisplayName.Equals(t.DisplayName));
        }

        /// <summary>
        /// Returns the hashcode for the value of this instance.
        /// </summary>
        /// <returns>Hashcode.</returns>
        public override int GetHashCode() {
            return Value.GetHashCode() ^ DisplayName.GetHashCode();
        }
    }

    /// <summary>
    /// Represents class storing the XmlStudio's settings.
    /// </summary>
    public class XmlStudioSettings : INotifyPropertyChanged {
        #region Fields
        private readonly ApplicationSettings applicationSettings;
        private static XmlStudioSettings instance = null;
        #endregion

        #region Constructors
        /// <summary>
        /// Gets the instance.
        /// </summary>
        /// <value>The instance.</value>
        public static XmlStudioSettings Instance {
            get {
                if(instance == null || (instance != null && instance.applicationSettings == null)) {
                    instance = new XmlStudioSettings();
                }

                return instance;
            }
        }

        private XmlStudioSettings() {
            this.applicationSettings = ApplicationSettings.Load();
        }
        #endregion

        #region Properties
        /// <summary>
        /// Gets or sets the default type of the layout algorithm.
        /// </summary>
        /// <value>The default type of the layout algorithm.</value>
        public LayoutAlgorithm DefaultLayoutAlgorithmType {
            get { return applicationSettings.DefaultLayoutAlgorithmType; }
            set {
                applicationSettings.DefaultLayoutAlgorithmType = value;
                this.OnPropertyChanged("DefaultLayoutAlgorithmType");
            }
        }

        /// <summary>
        /// Gets or sets the vertex default fill brush.
        /// </summary>
        /// <value>The vertex default fill brush.</value>
        public Brush VertexDefaultFillBrush {
            get { return applicationSettings.VertexDefaultFillBrush; }
            set {
                applicationSettings.VertexDefaultFillBrush = value;
                this.OnPropertyChanged("VertexDefaultFillBrush");
            }
        }

        /// <summary>
        /// Gets or sets the vertex default stroke brush.
        /// </summary>
        /// <value>The vertex default stroke brush.</value>
        public Brush VertexDefaultStrokeBrush {
            get { return applicationSettings.VertexDefaultStrokeBrush; }
            set {
                applicationSettings.VertexDefaultStrokeBrush = value;
                this.OnPropertyChanged("VertexDefaultStrokeBrush");
            }
        }

        /// <summary>
        /// Gets or sets the highlighted vertex background brush.
        /// </summary>
        /// <value>The highlighted vertex background brush.</value>
        public Brush HighlightedVertexBackgroundBrush {
            get { return applicationSettings.HighlightedVertexBackgroundBrush; }
            set {
                applicationSettings.HighlightedVertexBackgroundBrush = value;
                this.OnPropertyChanged("HighlightedVertexBackgroundBrush");
            }
        }

        /// <summary>
        /// Gets or sets the highlighted vertex border brush.
        /// </summary>
        /// <value>The highlighted vertex border brush.</value>
        public Brush HighlightedVertexBorderBrush {
            get { return applicationSettings.HighlightedVertexBorderBrush; }
            set {
                applicationSettings.HighlightedVertexBorderBrush = value;
                this.OnPropertyChanged("HighlightedVertexBorderBrush");
            }
        }

        /// <summary>
        /// Gets or sets the highlighted vertex foreground brush.
        /// </summary>
        /// <value>The highlighted vertex foreground brush.</value>
        public Brush HighlightedVertexForegroundBrush {
            get { return applicationSettings.HighlightedVertexForegroundBrush; }
            set {
                applicationSettings.HighlightedVertexForegroundBrush = value;
                this.OnPropertyChanged("HighlightedVertexForegroundBrush");
            }
        }

        /// <summary>
        /// Gets or sets the selected vertex background brush.
        /// </summary>
        /// <value>The selected vertex background brush.</value>
        public Brush SelectedVertexBackgroundBrush {
            get { return applicationSettings.SelectedVertexBackgroundBrush; }
            set {
                applicationSettings.SelectedVertexBackgroundBrush = value;
                this.OnPropertyChanged("SelectedVertexBackgroundBrush");
            }
        }

        /// <summary>
        /// Gets or sets the semi highlighted source vertex border brush.
        /// </summary>
        /// <value>The semi highlighted source vertex border brush.</value>
        public Brush SemiHighlightedSourceVertexBorderBrush {
            get { return applicationSettings.SemiHighlightedSourceVertexBorderBrush; }
            set {
                applicationSettings.SemiHighlightedSourceVertexBorderBrush = value;
                this.OnPropertyChanged("SemiHighlightedSourceVertexBorderBrush");
            }
        }

        /// <summary>
        /// Gets or sets the semi highlighted source vertex background brush.
        /// </summary>
        /// <value>The semi highlighted source vertex background brush.</value>
        public Brush SemiHighlightedSourceVertexBackgroundBrush {
            get { return applicationSettings.SemiHighlightedSourceVertexBackgroundBrush; }
            set {
                applicationSettings.SemiHighlightedSourceVertexBackgroundBrush = value;
                this.OnPropertyChanged("SemiHighlightedSourceVertexBackgroundBrush");
            }
        }

        /// <summary>
        /// Gets or sets the semi highlighted target vertex border brush.
        /// </summary>
        /// <value>The semi highlighted target vertex border brush.</value>
        public Brush SemiHighlightedTargetVertexBorderBrush {
            get { return applicationSettings.SemiHighlightedTargetVertexBorderBrush; }
            set {
                applicationSettings.SemiHighlightedTargetVertexBorderBrush = value;
                this.OnPropertyChanged("SemiHighlightedTargetVertexBorderBrush");
            }
        }

        /// <summary>
        /// Gets or sets the semi highlighted target vertex background brush.
        /// </summary>
        /// <value>The semi highlighted target vertex background brush.</value>
        public Brush SemiHighlightedTargetVertexBackgroundBrush {
            get { return applicationSettings.SemiHighlightedTargetVertexBackgroundBrush; }
            set {
                applicationSettings.SemiHighlightedTargetVertexBackgroundBrush = value;
                this.OnPropertyChanged("SemiHighlightedTargetVertexBackgroundBrush");
            }
        }

        /// <summary>
        /// Gets or sets the highlighted edge brush.
        /// </summary>
        /// <value>The highlighted edge brush.</value>
        public Brush HighlightedEdgeBrush {
            get { return applicationSettings.HighlightedEdgeBrush; }
            set {
                applicationSettings.HighlightedEdgeBrush = value;
                this.OnPropertyChanged("HighlightedEdgeBrush");
            }
        }

        /// <summary>
        /// Gets or sets the semi highlighted in edge brush.
        /// </summary>
        /// <value>The semi highlighted in edge brush.</value>
        public Brush SemiHighlightedInEdgeBrush {
            get { return applicationSettings.SemiHighlightedInEdgeBrush; }
            set {
                applicationSettings.SemiHighlightedInEdgeBrush = value;
                this.OnPropertyChanged("SemiHighlightedInEdgeBrush");
            }
        }

        /// <summary>
        /// Gets or sets the semi highlighted out edge brush.
        /// </summary>
        /// <value>The semi highlighted out edge brush.</value>
        public Brush SemiHighlightedOutEdgeBrush {
            get { return applicationSettings.SemiHighlightedOutEdgeBrush; }
            set {
                applicationSettings.SemiHighlightedOutEdgeBrush = value;
                this.OnPropertyChanged("SemiHighlightedOutEdgeBrush");
            }
        }

        private List<LanguageSetting> languages = new List<LanguageSetting>();
        /// <summary>
        /// Gets the languages.
        /// </summary>
        /// <value>The languages.</value>
        public ICollection<LanguageSetting> Languages {
            get { return this.languages; }
        }

        /// <summary>
        /// Gets or sets the language.
        /// </summary>
        /// <value>The language.</value>
        public LanguageSetting Language {
            get { return applicationSettings.Language; }
            set {
                applicationSettings.Language = value;
                this.OnPropertyChanged("Language");
            }
        }

        /// <summary>
        /// Gets or sets the node count to expand.
        /// </summary>
        /// <value>The node count to expand.</value>
        public int NodeCountToExpand {
            get { return applicationSettings.NodeCountToExpand; }
            set {
                applicationSettings.NodeCountToExpand = value;
                this.OnPropertyChanged("NodeCountToExpand");
            }
        }
        #endregion

        /// <summary>
        /// Saves this instance.
        /// </summary>
        public void Save() {
            this.applicationSettings.Save();
        }

        /// <summary>
        /// Occurs when a property value changes.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Called when property changed.
        /// </summary>
        /// <param name="propertyName">Name of the property.</param>
        public void OnPropertyChanged(string propertyName) {
            var propertyChanged = this.PropertyChanged;
            if(propertyChanged != null) {
                propertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }

    /// <summary>
    /// Class with settings that is serialized into file.
    /// </summary>
    [XmlInclude(typeof(SolidColorBrush))]
    [XmlInclude(typeof(MatrixTransform))]
    [Serializable]
    public class ApplicationSettings {
        private const string DefaultExceptionPolicy = "UIPolicy";

        #region Resources
        #region VertexDefaultFillBrush
        private static Brush vertexDefaultFillBrushResource = Application.Current != null ? Application.Current.TryFindResource("VertexDefaultFillBrush") as Brush : null;
        private static Brush vertexDefaultFillBrushDefault = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#FFE3E3E3"));
        #endregion

        #region VertexDefaultStrokeBrush
        private static Brush vertexDefaultStrokeBrushResource = Application.Current != null ? Application.Current.TryFindResource("VertexDefaultStrokeBrush") as Brush : null;
        private static Brush vertexDefaultStrokeBrushDefault = new SolidColorBrush(Colors.Black);
        #endregion

        #region HighlightedVertexBackgroundBrush
        private static Brush highlightedVertexBackgroundBrushResource = Application.Current != null ? Application.Current.TryFindResource("HighlightedVertexBackgroundBrush") as Brush : null;
        private static Brush highlightedVertexBackgroundBrushDefault = new SolidColorBrush(SystemColors.HighlightColor);
        #endregion

        #region HighlightedVertexBorderBrush
        private static Brush highlightedVertexBorderBrushResource = Application.Current != null ? Application.Current.TryFindResource("HighlightedVertexBorderBrush") as Brush : null;
        private static Brush highlightedVertexBorderBrushDefault = new SolidColorBrush(Colors.Transparent);
        #endregion

        #region HighlightedVertexForegroundBrush
        private static Brush highlightedVertexForegroundBrushResource = Application.Current != null ? Application.Current.TryFindResource("HighlightedVertexForegroundBrush") as Brush : null;
        private static Brush highlightedVertexForegroundBrushDefault = new SolidColorBrush(SystemColors.ControlLightLightColor);
        #endregion

        #region SelectedVertexBackgroundBrush
        private static Brush selectedVertexBackgroundBrushResource = Application.Current != null ? Application.Current.TryFindResource("SelectedVertexBackgroundBrush") as Brush : null;
        private static Brush selectedVertexBackgroundBrushDefault = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#FFC7D7FF"));
        #endregion

        #region SemiHighlightedSourceVertexBorderBrush
        private static Brush semiHighlightedSourceVertexBorderBrushResource = Application.Current != null ? Application.Current.TryFindResource("SemiHighlightedSourceVertexBorderBrush") as Brush : null;
        private static Brush semiHighlightedSourceVertexBorderBrushDefault = new SolidColorBrush(Colors.Transparent);
        #endregion

        #region SemiHighlightedSourceVertexBackgroundBrush
        private static Brush semiHighlightedSourceVertexBackgroundBrushResource = Application.Current != null ? Application.Current.TryFindResource("SemiHighlightedSourceVertexBackgroundBrush") as Brush : null;
        private static Brush semiHighlightedSourceVertexBackgroundBrushDefault = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#FF1DBA00"));
        #endregion

        #region SemiHighlightedTargetVertexBorderBrush
        private static Brush semiHighlightedTargetVertexBorderBrushResource = Application.Current != null ? Application.Current.TryFindResource("SemiHighlightedTargetVertexBorderBrush") as Brush : null;
        private static Brush semiHighlightedTargetVertexBorderBrushDefault = new SolidColorBrush(Colors.Transparent);
        #endregion

        #region SemiHighlightedTargetVertexBackgroundBrush
        private static Brush semiHighlightedTargetVertexBackgroundBrushResource = Application.Current != null ? Application.Current.TryFindResource("SemiHighlightedTargetVertexBackgroundBrush") as Brush : null;
        private static Brush semiHighlightedTargetVertexBackgroundBrushDefault = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#FFD6A51C"));
        #endregion

        #region HighlightedEdgeBrush
        private static Brush highlightedEdgeBrushResource = Application.Current != null ? Application.Current.TryFindResource("HighlightedEdgeBrush") as Brush : null;
        private static Brush highlightedEdgeBrushDefault = new SolidColorBrush(SystemColors.HighlightColor);
        #endregion

        #region SemiHighlightedInEdgeBrush
        private static Brush semiHighlightedInEdgeBrushResource = Application.Current != null ? Application.Current.TryFindResource("SemiHighlightedInEdgeBrush") as Brush : null;
        private static Brush semiHighlightedInEdgeBrushDefault = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#FF1DBA00"));
        #endregion

        #region SemiHighlightedOutEdgeBrush
        private static Brush semiHighlightedOutEdgeBrushResource = Application.Current != null ? Application.Current.TryFindResource("SemiHighlightedOutEdgeBrush") as Brush : null;
        private static Brush semiHighlightedOutEdgeBrushDefault = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#FFD6A51C"));
        #endregion
        #endregion

        #region Properties
        #region DefaultLayoutAlgorithmType
        private LayoutAlgorithm defaultAlgorithmType = LayoutAlgorithm.Tree;

        public LayoutAlgorithm DefaultLayoutAlgorithmType {
            get { return this.defaultAlgorithmType; }
            set {
                this.defaultAlgorithmType = value;
            }
        }
        #endregion

        #region VertexDefaultFillBrush
        private Brush vertexDefaultFillBrush = vertexDefaultFillBrushResource ?? vertexDefaultFillBrushDefault;

        public Brush VertexDefaultFillBrush {
            get { return this.vertexDefaultFillBrush; }
            set {
                this.vertexDefaultFillBrush = value;
            }
        }
        #endregion

        #region VertexDefaultStrokeBrush
        private Brush vertexDefaultStrokeColor = vertexDefaultStrokeBrushResource ?? vertexDefaultStrokeBrushDefault;

        public Brush VertexDefaultStrokeBrush {
            get { return this.vertexDefaultStrokeColor; }
            set {
                if(this.vertexDefaultFillBrush != value) {
                    this.vertexDefaultStrokeColor = value;
                }
            }
        }
        #endregion

        #region HighlightedVertexBackgroundBrush
        private Brush highlightedVertexBackgroundBrush = highlightedVertexBackgroundBrushResource ?? highlightedVertexBackgroundBrushDefault;

        public Brush HighlightedVertexBackgroundBrush {
            get { return this.highlightedVertexBackgroundBrush; }
            set {
                this.highlightedVertexBackgroundBrush = value;
            }
        }
        #endregion

        #region HighlightedVertexBorderBrush
        private Brush highlightedVertexBorderBrush = highlightedVertexBorderBrushResource ?? highlightedVertexBorderBrushDefault;

        public Brush HighlightedVertexBorderBrush {
            get { return this.highlightedVertexBorderBrush; }
            set {
                this.highlightedVertexBorderBrush = value;
            }
        }
        #endregion

        #region HighlightedVertexForegroundBrush
        private Brush highlightedVertexForegroundBrush = highlightedVertexForegroundBrushResource ?? highlightedVertexForegroundBrushDefault;

        public Brush HighlightedVertexForegroundBrush {
            get { return this.highlightedVertexForegroundBrush; }
            set {
                this.highlightedVertexForegroundBrush = value;
            }
        }
        #endregion

        #region SelectedVertexBackgroundBrush
        private Brush selectedVertexBackgroundBrush = selectedVertexBackgroundBrushResource ?? selectedVertexBackgroundBrushDefault;

        public Brush SelectedVertexBackgroundBrush {
            get { return this.selectedVertexBackgroundBrush; }
            set {
                this.selectedVertexBackgroundBrush = value;
            }
        }
        #endregion

        #region SemiHighlightedSourceVertexBorderBrush
        private Brush semiHighlightedSourceVertexBorderBrush = semiHighlightedSourceVertexBorderBrushResource ?? semiHighlightedSourceVertexBorderBrushDefault;

        public Brush SemiHighlightedSourceVertexBorderBrush {
            get { return this.semiHighlightedSourceVertexBorderBrush; }
            set {
                this.semiHighlightedSourceVertexBorderBrush = value;
            }
        }
        #endregion

        #region SemiHighlightedSourceVertexBackgroundBrush
        private Brush semiHighlightedSourceVertexBackgroundBrush = semiHighlightedSourceVertexBackgroundBrushResource ?? semiHighlightedSourceVertexBackgroundBrushDefault;

        public Brush SemiHighlightedSourceVertexBackgroundBrush {
            get { return this.semiHighlightedSourceVertexBackgroundBrush; }
            set {
                this.semiHighlightedSourceVertexBackgroundBrush = value;
            }
        }
        #endregion

        #region SemiHighlightedTargetVertexBorderBrush
        private Brush semiHighlightedTargetVertexBorderBrush = semiHighlightedTargetVertexBorderBrushResource ?? semiHighlightedTargetVertexBorderBrushDefault;

        public Brush SemiHighlightedTargetVertexBorderBrush {
            get { return this.semiHighlightedTargetVertexBorderBrush; }
            set {
                this.semiHighlightedTargetVertexBorderBrush = value;
            }
        }
        #endregion

        #region SemiHighlightedTargetVertexBackgroundBrush
        private Brush semiHighlightedTargetVertexBackgroundBrush = semiHighlightedTargetVertexBackgroundBrushResource ?? semiHighlightedTargetVertexBackgroundBrushDefault;

        public Brush SemiHighlightedTargetVertexBackgroundBrush {
            get { return this.semiHighlightedTargetVertexBackgroundBrush; }
            set {
                this.semiHighlightedTargetVertexBackgroundBrush = value;
            }
        }
        #endregion

        #region HighlightedEdgeBrush
        private Brush highlightedEdgeBrush = highlightedEdgeBrushResource ?? highlightedEdgeBrushDefault;

        public Brush HighlightedEdgeBrush {
            get { return this.highlightedEdgeBrush; }
            set {
                this.highlightedEdgeBrush = value;
            }
        }
        #endregion

        #region SemiHighlightedInEdgeBrush
        private Brush semiHighlightedInEdgeBrush = semiHighlightedInEdgeBrushResource ?? semiHighlightedInEdgeBrushDefault;

        public Brush SemiHighlightedInEdgeBrush {
            get { return this.semiHighlightedInEdgeBrush; }
            set {
                this.semiHighlightedInEdgeBrush = value;
            }
        }
        #endregion

        #region SemiHighlightedOutEdgeBrush
        private Brush semiHighlightedOutEdgeBrush = semiHighlightedOutEdgeBrushResource ?? semiHighlightedOutEdgeBrushDefault;

        public Brush SemiHighlightedOutEdgeBrush {
            get { return this.semiHighlightedOutEdgeBrush; }
            set {
                this.semiHighlightedOutEdgeBrush = value;
            }
        }
        #endregion

        #region Language
        private LanguageSetting language = new LanguageSetting { Value = "en-US", DisplayName = "English (United States)" };

        /// <summary>
        /// Gets or sets the application's language.
        /// </summary>
        public LanguageSetting Language {
            get { return this.language; }
            set { this.language = value; }
        }
        #endregion

        #region NodeCountToExpand
        private int nodeCountToExpand = 10;

        /// <summary>
        /// Gets or sets the number of nodes which an element can have so it is automatically expanded (without asking).
        /// </summary>
        public int NodeCountToExpand {
            get { return this.nodeCountToExpand; }
            set { this.nodeCountToExpand = value; }
        }
        #endregion
        #endregion

        /// <summary>
        /// Saves this instance.
        /// </summary>
        public void Save() {
            IsolatedStorageFileStream stream = null;
            try {
                var storage = IsolatedStorageFile.GetStore(IsolatedStorageScope.User | IsolatedStorageScope.Assembly, null, null);
                stream = new IsolatedStorageFileStream("XmlStudio.settings", System.IO.FileMode.Create, storage);
                var serializer = new XmlSerializer(typeof(ApplicationSettings));
                serializer.Serialize(stream, this);
            } catch(Exception ex) {
                bool rethrow = ExceptionPolicy.HandleException(ex, DefaultExceptionPolicy);
                if(rethrow) {
                    throw;
                }
            } finally {
                if(stream != null) {
                    stream.Close();
                }
            }
        }

        /// <summary>
        /// Loads this instance.
        /// </summary>
        /// <returns></returns>
        public static ApplicationSettings Load() {
            ApplicationSettings settings = null;
            IsolatedStorageFileStream stream = null;
            try {
                var storage = IsolatedStorageFile.GetStore(IsolatedStorageScope.User | IsolatedStorageScope.Assembly, null, null);
                stream = new IsolatedStorageFileStream("XmlStudio.settings", System.IO.FileMode.OpenOrCreate, storage);
                if(stream.Length > 0) {
                    var serializer = new XmlSerializer(typeof(ApplicationSettings));
                    settings = (ApplicationSettings)serializer.Deserialize(stream);
                } else {
                    settings = new ApplicationSettings();
                }
            } catch(Exception ex) {
                Logger.Writer.Write(ex.ToString(), "Exceptions", 100, 0, TraceEventType.Error);
                settings = new ApplicationSettings();
            } finally {
                if(stream != null) {
                    stream.Close();
                }
            }

            return settings;
        }
    }
}